#include <iostream>
#include <stdlib.h>
#include <string>
#include <time.h>
using namespace std;

const int Total = 10; // 生产/消费缓存区最大数量
const int Num = 5;    // 表示一个进程的指令总数

enum ProcessEnum
{
    Run,
    Ready,
    Wait,
    Finish,
};

string printEnum(ProcessEnum e)
{
    switch (e)
    {
    case Run:
        return "Run";
        break;
    case Ready:
        return "Ready";
        break;
    case Wait:
        return "Wait";
        break;
    case Finish:
        return "Finish";
        break;
    default:
        return "nothing";
    }
}

struct PCB
{
    string name;              // 进程名
    ProcessEnum state;        // 进程状态
    string waitReason;        // 表示S1和S2 Full为s1, Empty为s2
    int breakPoint;           // 断点
    string instructions[Num]; // 指令

} producer, consumer, *curProcess; // 定义生产者，消费者PCB和指针

int Empty, Full;         // 信号量，empty当前资源可存量 full当前资源可取量
int PC;                  // 指令计数器
int Buffer[Total] = {0}; // 缓冲区,产品用数字代替
int BufferIndex = 0;     // 缓冲区指针
int consume_tmp = 0;     // 同下
int produce_tmp = 0;     // 因为生产和存放不在同一条指令进行，所以生产后将产品暂存

// P操作
void P(int &samphore, string samphoreName, PCB *pcb)
{
    printf("%s进行P操作的信号量为%s = %d\n", curProcess->name.c_str(), samphoreName.c_str(), samphore);
    --samphore;
    if (samphore < 0)
    {
        pcb->state = Wait;
        pcb->waitReason = samphoreName;
        cout << pcb->name << "  开始等待" << endl;
    }
    else
    {
        pcb->state = Ready;
        cout << pcb->name << " 无需等待" << endl;
    }
}

void V(int &samphore, string samphoreName, PCB *pcb)
{
    ++samphore;
    if (samphore <= 0)
    {
        // 如果这个信号量是producer等待的，就释放producer
        if (producer.waitReason == samphoreName)
            producer.state = Ready;
        else if (consumer.waitReason == samphoreName)
            consumer.state = Ready;
        if (curProcess->name == "Producer")
            cout << "消费者就绪" << endl;
        else
            cout << "生产者就绪" << endl;
    }
    pcb->state = Ready;
}

void initial() // 初始化各参数
{
    // name
    producer.name = "Producer";
    consumer.name = "Consumer";
    // 指令标识符数组初始化
    producer.instructions[0] = "Produce";
    producer.instructions[1] = "P(Empty)";
    producer.instructions[2] = "PUT";
    producer.instructions[3] = "V(Full)";
    producer.instructions[4] = "Goto 0";
    consumer.instructions[0] = "P(Full)";
    consumer.instructions[1] = "GET";
    consumer.instructions[2] = "V(Empty)";
    consumer.instructions[3] = "Consume";
    consumer.instructions[4] = "Goto 0";
    // PC初始化
    PC = 0;
    // 状态初始化
    producer.state = Ready;
    consumer.state = Ready;
    // 资源初始化
    Empty = 10;
    Full = 0;
    // 断点初始化
    producer.breakPoint = 0;
    consumer.breakPoint = 0;
    // 等待原因初始化
    producer.waitReason = "Not Wait";
    consumer.waitReason = "Not Wait";
}

void show_Buffer()
{
    int i = 0;
    for (i = 0; i < Total; i++)
    {
        printf("%d ", Buffer[i]);
    }
}

void show_PCB(PCB &pcb)
{
    printf("Name:%s\n", pcb.name.c_str());
    printf("State:%s\n", printEnum(pcb.state).c_str());
    if (pcb.state == Wait)
        cout << "WaitReason : wait for " << pcb.waitReason << "samphore." << endl;
    printf("Point:%d\n\n", pcb.breakPoint);
}

void Running() // 模拟处理器执行
{
    int flag = curProcess->name[0] == 'C'; // flag 0:生产者 1:消费者
    cout << curProcess->name << "当前执行指令 : " << curProcess->instructions[PC] << endl;
    // 执行过程
    switch (PC++)
    {

    case 0:
        if (flag == 0) // 生产者执行【produce】
        {
            printf("【提示】请输入一件产品:");
            scanf("%d", &produce_tmp); // 产品暂存
        }
        else // 消费者【P(S2)】
            P(Full, "Full", &consumer);
        break;

    case 1:
        if (flag == 0) // 生产者【P(s1)】
            P(Empty, "Empty", &producer);
        else // 消费者【GET】
        {
            printf("消费者从缓冲区取一个产品\n");
            consume_tmp = Buffer[BufferIndex - 1]; //-1是因为生产者生产后+1
            Buffer[BufferIndex - 1] = 0;           // 被取走后，缓冲区为0
            BufferIndex = (BufferIndex - 1) % Total;
            curProcess->state = Ready;
        }
        break;

    case 2:
        if (flag == 0) // 生产者【PUT】
        {
            printf("生产者产入一个商品");
            Buffer[BufferIndex] = produce_tmp;
            BufferIndex = (BufferIndex + 1) % Total;
            curProcess->state = Ready; // 设置为就绪态
        }
        else // 消费者【V(s1)】
            V(Empty, "Empty", &consumer);
        break;

    case 3:
        if (flag == 0) // 生产者释放full【V(s2)】
            V(Full, "Full", &producer);
        else // 消费者【consume】--打印
            printf("消费了一个产品:%d\n", consume_tmp);
        break;

    case 4:
        PC = 0;
        if (flag == 0)
            cout << "Producer一次此次生产执行完毕" << endl;
        cout << curProcess->name << " Goto 0" << endl;
        break;
    }
}

void Scheduling() // 处理器调度程序--随机选择出执行阶段的currentPC
{
    while (producer.state == Ready || consumer.state == Ready)
    {
        if (producer.state == Ready && consumer.state == Ready)
        {
            // srand(time(NULL));
            // int randNum = rand() % 2;//随机调度不方便观察一些情况
            cout << "两者均为就绪状态，请选择 1 producer, 0 consumer" << endl;
            int randNum;
            cin >> randNum;
            curProcess = randNum ? &producer : &consumer;
        }
        else
        {
            if (producer.state == Ready)
                curProcess = &producer;
            else
                curProcess = &consumer;
            cout << "只有一个进程就绪，已自动选择就绪的进程 : " << curProcess->name << endl;
        }

        cout << "调度机选中的进程是" << curProcess->name << endl;
        // 设置currentPC的参数
        PC = curProcess->breakPoint;
        Running(); // 执行进程
        curProcess->breakPoint = PC;

        printf("当前缓冲区为:");
        show_Buffer();
        printf("\n当前Empty值为:%d 当前Full值为:%d\n\n", Empty, Full);
        show_PCB(producer);
        show_PCB(consumer);
        cout << "输入任意字符继续执行" << endl;
        cin.ignore();
        cin.get();
        printf("-----分割线-----\n\n");
    }
    cout << "无就绪进程, 模拟程序退出" << endl;
}

int main()
{
    initial();    // 初始化
    Scheduling(); // 循环执行调度器
    return 0;
}